package main

import (
	"fmt"
	"os"
	"sort"
	"text/tabwriter"
	"time"
)

type Worker struct {
	Name     string
	Age      int8
	Salary   float32
	WorkTime time.Duration
}

func length(s string) time.Duration {
	d, err := time.ParseDuration(s)
	if err != nil {
		panic(err)
	}
	return d
}

var workers = []*Worker{
	{"Tom", 31, 26000, length("10h")},
	{"Jenny", 31, 20000, length("12h")},
	{"Jenny", 32, 22000, length("1h")},
}

func printWorkers(workers []*Worker) {
	const format = "%v\t%v\t%v\t%v\t\n"
	tw := new(tabwriter.Writer).Init(os.Stdout, 0, 8, 2, ' ', 0)
	fmt.Fprintf(tw, format, "Name", "Age", "Salary", "WorkTime")
	fmt.Fprintf(tw, format, "-----", "------", "-----", "----")
	for _, w := range workers {
		fmt.Fprintf(tw, format, w.Name, w.Age, w.Salary, w.WorkTime)
	}
	tw.Flush() // calculate column widths and print table
}

type bySalary []*Worker

func (x bySalary) Len() int {
	return len(x)
}

func (x bySalary) Less(i, j int) bool {
	return !(x[i].Salary < x[j].Salary)
}

func (x bySalary) Swap(i, j int) {
	x[i], x[j] = x[j], x[i]
}

type byWorkTime []*Worker

func (x byWorkTime) Len() int {
	return len(x)
}

func (x byWorkTime) Less(i, j int) bool {
	return !(x[i].WorkTime.Seconds() < x[j].WorkTime.Seconds())
}

func (x byWorkTime) Swap(i, j int) {
	x[i], x[j] = x[j], x[i]
}

type customSort struct {
	w    []*Worker
	less func(x, y *Worker) bool
}

func (x customSort) Len() int           { return len(x.w) }
func (x customSort) Less(i, j int) bool { return x.less(x.w[i], x.w[j]) }
func (x customSort) Swap(i, j int)      { x.w[i], x.w[j] = x.w[j], x.w[i] }

func main() {
	fmt.Println("bySalary:")
	sort.Sort(bySalary(workers))
	printWorkers(workers)

	fmt.Println("byWorkTime:")
	sort.Sort(byWorkTime(workers))
	printWorkers(workers)

	fmt.Println("customSort:")
	sort.Sort(customSort{workers, func(x, y *Worker) bool {
		if x.Name != y.Name {
			return x.Name < y.Name
		}
		if x.Age != y.Age {
			return x.Age < y.Age
		}
		if x.Salary != y.Salary {
			return x.Salary < y.Salary
		}
		if x.WorkTime.Seconds() != y.WorkTime.Seconds() {
			return x.WorkTime.Seconds() < y.WorkTime.Seconds()
		}
		return false
	}})
	printWorkers(workers)
}
